/* msg.c : messaging system (assignment 2)
 */

#include <kernel.h>
pcb* getPCBfromPID(int pid){
	/* Find the pcb for the process requested */
	pcb* index = pcbTable;
	while (index != NULL){
		if (index->pid == pid) return index;
		index = index->next;
	}
	/* Requested process does not exist */
	return NULL;
}

/* Send data to another process. Returns -1 if fail, or the number of bytes sent. 
Blocks untill the send operation completes. 
If the toProc is not alreayd blocked waiting for THIS send, then block thisProc until it is.
Else copy the message into the toProc's buffer. Then ready() both. 

If a send() to a process is blocked, and the target of the send terminates, then send() 
will return a −1 to indicate that the PID is invalid. 

Similarly, if a recv() is blocked waiting for a message from a specific process and 
that process terminates then −1 is returned by recv().

Since it is possible that the buffer sizes of the sender and receiver do not match, 
the kernel will have to ensure that data is not copied or read beyond the end of a buffer.*/
int send (int toPID, int fromPID, void* buffer, int buffer_len){
	int numBytesSent = -1;
	
	pcb* toProc 	= getPCBfromPID(toPID);
	pcb* thisProc 	= getPCBfromPID(fromPID);
	
// FILL ME IN
	/* Check if the target is blocked AND waiting for this to send to it. If so, do the send, and ready() both. */
	if(toProc->msgWaitingForPID == fromPID){
		verbose("send: Target was waiting for me to send. Commencing...\n");
		// SEND HAPPENS
		// READY BOTH
		// CALC NUMBYTES
	}
	return numBytesSent;
}

/* 	Recieve data from another process. Blocks untill there is a message to recieve from ANY process. 
	Returns num bytes receieved, else -1 on fail. 
	The 'from' pointer value is changed to identify the sender process. */
/*	The sysrecv() system call takes a pointer to an integer specifying the process to receive from, 
	a destination buffer, and the size of the buffer as arguments. 
	
	If the PID to receive is non-zero, the system call blocks until it receives a message from the corresponding process. 
	
	If the PID to receive from is 0, the system call blocks until it receives a message from any process. 
	(Observe, this has an effect on what PIDs are allowed in the system.) 
	
	The system call returns the number of bytes that were received, or −1 if the PID to receive from is invalid,
	or does not exist. 
	
	In addition the memory location pointed to by the from address parameter 
	is set to the PID of the process that sent the message. (Note an actual update to this field is only required 
	when the PID to receive from is set to 0.) */
int recv (int* fromPID, int toPID, void* buffer, int buffer_len){
	if (*fromPID < 0) {
		verbose("recv: Error! Requested negative fromPID!\n");
		return -1; /* Don't allow negative PIDs */
	}
	int numBytesRead = -1;
	pcb* thisProc 	= getPCBfromPID(toPID);
	if (!thisProc) {
		verbose("recv: Error! THIS process not found in pcb table!\n");
		return -1;
	}
	
	/* Make sure that there exists a process associated with the fromPID, if it was specified. */
	if (*fromPID != 0) {
		pcb* fromProc = getPCBfromPID(*fromPID);
		if (fromProc == NULL) {
			verbose("recv: Error! Specific, yet nonexistant from PID specified!\n");
			return -1; /* Nonexistant process specified */
		}
	}
	
	/* If accepting any message, pop the first waiting process off the queue, 
	   else scan the queue for the desired sending process. */
	pcb* fromProc = findInQueue(thisProc->msgQueue, *fromPID);

	if (fromProc == NULL){
		/* The desired process has still not yet placed itself in the msgQueue, or it was empty. */
		/* Block untill someone attempts to message me, wait for anyone if fromPID is 0*/
		verbose("recv: FromProc not found in msgQueue, or queue empty. Blocking %d, waiting on %d.\n", toPID, fromPID);
		thisProc->msgWaitingForPID = *fromPID;
		thisProc->state = BLOCKED;
	}
	else {
		/* Now the desired sending process is fromProc. */
		verbose("recv: FromProc %d found in msgQueue. Blocking myself:%d, so it can send.\n", fromProc->pid, toPID);
		*fromPID = fromProc->pid;
		thisProc->msgWaitingForPID = *fromPID;
		thisProc->state = BLOCKED;
	}
	
	// What now? Disp check if this blocked and then treat as yield for now??? How do I get this proc to know the transfer completed?
		
	return numBytesRead;
}